//
// This code was created by Jeff Molofee '99
// (ported to SDL by Sam Lantinga '2000)
//
// If you've found this code useful, please let me know.
//
// Visit me at www.demonews.com/hosted/nehe 
//
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#if defined(__APPLE__) && defined(__MACH__)
#include <OpenGL/gl.h>	// Header File For The OpenGL32 Library
#include <OpenGL/glu.h>	// Header File For The GLu32 Library
#else
#include <GL/gl.h>	// Header File For The OpenGL32 Library
#include <GL/glu.h>	// Header File For The GLu32 Library
#endif
#include "SDL.h"

#define W_ 800
#define H_ 600

#define W_2 (W_ / 2)
#define H_2 (H_ / 2)

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>

#include "bsp_reader.h"
#include "bsp2zone.h"
#include "segment.h"
#include "zone.h"

GLuint disp_list;
typedef struct
{
	float x;
	float y;
	float z;
	float w;
} pos_t;

pos_t zRot__ = {0, 0, 1, 0};
pos_t xRot__ = {1, 0, 0, 0};
pos_t yRot__ = {0, 1, 0, 0};
pos_t pos__ = {0, 0, 0, 0};

void InitGL(int Width, int Height)
{
	glViewport(0, 0, Width, Height);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(90.0, (float)W_ / (float)H_, 0.1, 4096.0);

	glMatrixMode(GL_MODELVIEW);
	glEnable(GL_DEPTH_TEST);
}

void DrawGLScene()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

        glRotatef(zRot__.w, zRot__.x, zRot__.y, zRot__.z);
	glRotatef(xRot__.w, xRot__.x, xRot__.y, xRot__.z);
	glRotatef(yRot__.w, yRot__.x, yRot__.y, yRot__.z);
	glTranslatef(pos__.x, pos__.y, pos__.z);

	glBegin(GL_LINES);
	glColor3f(1.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(10.0, 0.0, 0.0);
	glColor3f(0.0, 1.0, 0.0);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 10.0, 0.0);
	glColor3f(0.0, 0.0, 1.0);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, 10.0);
	glEnd();

	glColor3f(0.5, 0.0, 1.0);

	glLineWidth(2.0);
	glCallList(disp_list);

	SDL_GL_SwapBuffers();
}

void load_the_damn_bsp(const std::string& file)
{
	std::vector<int> models_needed;
	Bsp b;
	b.load(file);
	bsp2zone b2;
	b2.computeZones(b);
	b2.computeZoneNeighbors();


	disp_list = glGenLists(1);
	glNewList(disp_list, GL_COMPILE);

	std::vector<zone>::iterator it = b2.getZones().vertexes().begin();
	glLineWidth(1.0);
	while (it != b2.getZones().vertexes().end())
	{
		glDepthFunc(GL_LESS);
		glColor3f(0.5, 0.0, 1.0);
		glBegin(GL_QUADS);
		for (int i=0; i<4; i++)
		{
			glVertex3f( it->getPoints()[i].X,
				    it->getPoints()[i].Y,
				    -it->getPoints()[i].Z );
		}
		glEnd();
		
		glDepthFunc(GL_LEQUAL);
		glColor3f(0.0, 0.5, 1.0);
		glBegin(GL_LINE_LOOP);
		for (int i=0; i<4; i++)
		{
			glVertex3f( it->getPoints()[i].X,
				    it->getPoints()[i].Y,
				    -it->getPoints()[i].Z );
		}
		glEnd();

		it++;
	}

	std::list<segment_t>::iterator it2 = b2.tmp____.begin();
	glLineWidth(4.0);
	glBegin(GL_LINES);
	while (it2 != b2.tmp____.end())
	{
		glColor3f(1.0f, 0.0f, 0.0f);
		glVertex3f(it2-> first.X, it2-> first.Y, -it2-> first.Z);
		glVertex3f(it2->second.X, it2->second.Y, -it2->second.Z);
		it2++;
	}
	glEnd();

	glEndList();
}

int main(int argc, char **argv) 
{  
	try
	{
		srand(time(NULL));
		if ( SDL_Init(SDL_INIT_VIDEO) < 0 )
		{
			fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
			exit(1);
		}

		if ( SDL_SetVideoMode(W_, H_, 0, SDL_OPENGL) == NULL )
		{
			fprintf(stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError());
			SDL_Quit();
			exit(2);
		}

		SDL_WM_SetCaption("bloube", NULL);

		InitGL(W_, H_);
		int done = 0;

		std::string map = "../baseq3/maps/";
		if (argc == 2)
			map += argv[1];
		else
			map += "way_test";

		load_the_damn_bsp(map + ".bsp");

		while (!done)
		{
			int mouse_x;
			int mouse_y;
			Uint8* keys;

			DrawGLScene();

			// Events handling
			{
				SDL_PumpEvents();
				SDL_GetRelativeMouseState(&mouse_x, &mouse_y);

				if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON)
				{
					xRot__.w += mouse_y;
					yRot__.w += mouse_x;
				}

				keys = SDL_GetKeyState(NULL);
				if (keys[SDLK_ESCAPE])
					done = 1;
				if (keys[SDLK_g])
				{
					SDL_WM_GrabInput(SDL_GRAB_ON);
					SDL_ShowCursor(SDL_DISABLE);
				}
				if (keys[SDLK_h])
				{
					SDL_WM_GrabInput(SDL_GRAB_OFF);
					SDL_ShowCursor(SDL_ENABLE);
				}
				if (keys[SDLK_UP])
				{
					pos__.x += (float)std::sin(-yRot__.w * 0.0174532925f) * 10.0;
					pos__.z += (float)std::cos(-yRot__.w * 0.0174532925f) * 10.0;
					pos__.y += (float)std::sin(xRot__.w * 0.0174532925f) * 10.0;
				}
				if (keys[SDLK_DOWN])
				{
					pos__.x -= (float)std::sin(-yRot__.w * 0.0174532925f) * 10.0;
					pos__.z -= (float)std::cos(-yRot__.w * 0.0174532925f) * 10.0;
					pos__.y -= (float)std::sin(xRot__.w * 0.0174532925f) * 10.0;
				}
				if (keys[SDLK_LEFT])
				{
					pos__.x -= (float)std::sin(-yRot__.w * 0.0174532925f - M_PI/2.0) * 10.0;
					pos__.z -= (float)std::cos(-yRot__.w * 0.0174532925f - M_PI/2.0) * 10.0;
				}
				if (keys[SDLK_RIGHT])
				{
					pos__.x += (float)std::sin(-yRot__.w * 0.0174532925f - M_PI/2.0) * 10.0;
					pos__.z += (float)std::cos(-yRot__.w * 0.0174532925f - M_PI/2.0) * 10.0;
				}

			}

		}

		SDL_Quit();

	}
	catch(Exception e)
	{
		std::cerr << "Something bad happened: " << e << std::endl;
	}

	return 0;
}

